/**
 * 根据下面Promise的一些特性，手写一个DefPromise
 *
 *Promise对象表示异步操作的最终完成（或失败）及其结果值。
 * 状态：
 *pending：初始状态，既未实现也不被拒绝。
 *fulfilled：表示操作成功完成
 *rejected：表示操作失败。
 *
 *实例方法:
 * then()
 *catch()
 *
 * 静态方法:
 * Promise.all(iterable)
 * Promise.race(iterable)
 * Promise.reject(reason)
 * Promise.resolve(value)
 *
 */

class DefPromise{
    constructor (fn) {
        // 三个状态
        this.status = 'pending' // promise状态属性，初始值是pending
        this.data = undefined // 存储结构数据的属性
        this.callbacks = [] // 每个元素的结构{onResolved(){},onRejected(){}}

        let resolve = (value)=>{

            //如果当前状态不是pending，直接结束
            if(this.status !== 'pending'){
                return
            }
            //将状态改为resolved
            this.status = 'resolved'
            //保存value数据
            this.data = value
            //如果有待执行的callback函数，立即异步执行回调onResolved
            if(this.callbacks.length > 0){
                setTimeout(()=>{ //放入队列中执行成功所有的回调，这里setTimeout是模拟队列
                    this.callbacks.forEach(callbacksObj => {
                        callbacksObj.onResolved(value)
                    })
                })
            }
        }

        let reject = (reason)=>{
            //如果当前状态不是pending，直接结束
            if(this.status !== 'pending'){
                return
            }
            //将状态改为 rejected
            this.status = 'rejected'
            //保存value数据
            this.data = reason
            //如果有待执行的callback函数，立即异步执行回调onRejected
            if(this.callbacks.length > 0){
                setTimeout(()=>{ //放入队列中执行成功所有的回调，这里setTimeout是模拟队列
                    this.callbacks.forEach(callbacksObj => {
                        callbacksObj.onRejected(reason)
                    })
                })
            }
        }

        //立即同步执行
        try{
            fn(resolve,reject)
        }catch (error){
            reject(error)
        }
    }
    /**
     *
     * @param onResolved
     * @param onRejected
     * 指定一个成功和失败的回调函数
     * 返回一个新的promise对象
     * 返回promise的结果由onResolved或者onRejected执行结果决定
     */
     then(onResolved, onRejected) {
         //判断 onResolved, onRejected,指定函数的默认值
        onResolved = typeof onResolved ==='function' ? onResolved : value => value //向后传递成功的value
         //指定默认的失败的回调（实现错误/异常传递的关键）
        onRejected = typeof onRejected ==='function' ? onRejected : reason => {throw reason} //向后传递失败的reason

         //返回一个新的 promise
        return new DefPromise((resolve,reject)=>{
            /*
               当前promise的状态可能是resolved、pending、rejected
             */

            //调用指定的callback处理，根据执行的结果，改变return的promise的状态
            const __handleCallback=(callback)=>{
                /*
                    1.可能抛出异常
                    2.callback返回值可能是promise类型的
                    2.callback返回值可能不是promise类型的
                 */
                try{
                    const result = callback(this.data)
                    //如果回调函数onResolved返回的是promise，return的promise结果是这个promise的结果
                    if(result instanceof DefPromise){
                        //result成功式return的promise也成功，否则也失败
                        result.then(resolve, reject)
                    }else{
                        //如果onResolved执行返回非promise，return的promise就会成功，value就是返回值
                        resolve(result)
                    }
                }catch(error){
                    //如果执行抛出异常，return的promise就会失败,reason就是error
                    reject(error)
                }
            }

            //当前状态还是pending状态，将回调函数保存起来
            if(this.status === 'pending'){
                this.callbacks.push({
                    onResolved(value){
                        __handleCallback(onResolved)
                    },
                    onRejected(reason){
                        __handleCallback(onRejected)
                    }
                })
            }
            //当前是resolved状态，异步执行 onResolved 并且改变 return 的 promise 的状态
            else if(this.status === 'resolved'){
                setTimeout(()=>{
                    __handleCallback(onResolved)
                })
            }
            //当前是rejected状态，异步执行 onRejected 并且改变 return 的 promise 的状态
            else{
                setTimeout(()=>{
                    __handleCallback(onRejected)
                })
            }
        })

    }
    /**
     *
     * @param onRejected
     * 指定失败的回调函数
     */
    catch(onRejected){
       return this.then(undefined,onRejected)
    }

    /**
     *
     * @param value
     * 返回一个指定结果的成功的promise
     * 如果是一般值，Promise成功时，value就是这个值
     * 如果是一个成功的 promise 的值，Promise成功，value是这个Promise的value
     * 如果是一个失败的 promise 的值，Promise失败，reason是这个Promise的reason
     */
    //这个静态方法resolve让构造函数本身调用
    static resolve(value){
        //返回一个成功或者失败的promise
        return new DefPromise((resolve,reject)=>{
            // value 是 promise
            if(value instanceof DefPromise){ //使用value的结果作为promise的结果
                value.then(resolve,reject)
            }
            // value 不是 promise类型,promise变为成功，数据是value
            else{
                resolve(value)
            }
        })
    }
    /**
     *
     * @param reason
     * 返回一个指定reason的失败的promise
     */
    static reject(reason){
        //返回一个失败的promise
        return new DefPromise((resolve,reject)=>{
            reject(reason)
        })
    }

    /**
     *
     * @param promisesArr
     * 返回一个promise,只有当所有promise都成功才会成功，否则就是失败
     */
    static all(promisesArr){
        // 保存所有成功value,并且指定长度的数组
        const values = new Array(promisesArr.length)
        //用来保存promise成功的数量
        let resolveCount = 0
        return new DefPromise((resolve,reject) => {
            //遍历获取每一个promise的结果
            promisesArr.forEach((p,index) => {
                //由于p不是一个promise类型的值，所以需要处理一下, DefPromise.resolve(p)保证是一个promise类型值
                DefPromise.resolve(p).then(
                    value => {
                        resolveCount++ //成功就加1
                        //p成功，将成功的value按顺序保存到values
                        values[index] = value
                        //如果全部成功，将return的promise改为成功,计数器个数最终等于数组length则表示全部成功
                        if(resolveCount === promisesArr.length){
                            resolve(values)
                        }
                    },
                    reason =>{ //只要有一个失败了，return的promise就是失败,并且是第一个
                        //由于状态是只改变一次的，所以这里不需要判断是否是第一个失败，一有失败必定是第一个
                        reject(reason)
                    }
                )
            })

        })
    }

    /**
     *
     * @param promisesArr
     * 返回一个promise,其结果由第一个promise决定
     */
    static race(promisesArr){
        return new DefPromise((resolve,reject) => {
            //遍历获取每一个promise的结果
            promisesArr.forEach((p) => {
                //由于p不是一个promise类型的值，所以需要处理一下, DefPromise.resolve(p)保证是一个promise类型值
                DefPromise.resolve(p).then(
                    value => { //一旦有成功了，就return变为成功
                        resolve(value)
                    },
                    reason =>{//一旦有失败了，就return变为失败
                        reject(reason)
                    }
                )
            })

        })
    }

}





//下面是测试：
// test demo1
let promise = new DefPromise((resolve, reject)=>{
      setTimeout(()=>{
          reject('reject...')
      },100)
})

promise.then(
    value => {
        console.log("onResolved1()",value)
    },
    reason => {
        console.log("onRejected1()",reason) // onRejected1() reject...
        return new DefPromise((resolve,reject)=>reject('onRejected1+'))
    })
.then(
        value => {
            console.log("onResolved2()",value)
        },
        reason => {
            console.log("onRejected2()",reason) // onRejected2() onRejected1+
            throw 'err'
        })
.catch(reason=>{
    console.log("onRejected3()",reason) // onRejected3() err
})





// 测试异步 async await

function sleep(ms) {
    return new  DefPromise(function(resolve, reject) {
      setTimeout(()=>{
          resolve('sleep成功返回...')
        },ms)

    })
}
async function handle(){
        return await sleep(2000)
}
handle().then((value)=>{
    console.log(value) // sleep成功返回...
})





//测试静态方法resolve和reject

const p1 =  DefPromise.resolve(1)
const p2 =  DefPromise.resolve( DefPromise.resolve(2))
const p3 =  DefPromise.resolve( DefPromise.reject(3))

p1.then(value=>{
    console.log(value) // 1
})
p2.then(value=>{
    console.log(value) // 2
})
p3.catch(reason=>{
    console.log(reason) // 3
})




//测试静态方法all

const p4 = 4
const p5 = DefPromise.resolve(5)
const p6 = DefPromise.resolve(DefPromise.resolve(6))
const p7 = DefPromise.reject(7)

const pAll1 = DefPromise.all([p4,p5,p6])
pAll1.then(values =>{
        console.log('pAll1 resolve',values)  // pAll1 resolve [ 4, 5, 6 ]
    },
    reason =>{
        console.log('pAll1 reject',reason)
    }
)
const pAll2 = DefPromise.all([p4,p5,p6,p7])
pAll2.then(values =>{
        console.log('pAll2 resolve',values)
    },
    reason =>{
        console.log('pAll2 reject',reason) // pAll2 reject 7
    }
)




//测试静态方法race

const pRace1 = DefPromise.race([p4,p5,p6])
pRace1.then(values =>{
        console.log('pRace1 resolve',values)  // pRace1 resolve 4
    },
    reason =>{
        console.log('pRace1 reject',reason)
    }
)
const pRace2 = DefPromise.race([p7,p4,p5,p6])
pRace2.then(values =>{
        console.log('pRace2 resolve',values)
    },
    reason =>{
        console.log('pRace2 reject',reason) // pRace2 reject 7
    }
)
